// Copyright 2015-2024 Beken
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.


#include <string.h>
#include <stdlib.h>
#ifdef BK
#include <os/mem.h>
#include <os/str.h>
#endif
#include "wb_vad.h"
#include "vad_core.h"
#include "vad.h"
#ifdef BK
#include <common/bk_generic.h>
#include <components/system.h>
#endif

#include <assert.h>
#include <stdio.h>


static FILE *fidw = NULL;

static int head_speech_count= 8;// 默认起始语音帧数阈值
static int silence_vad_count = 4;
static int tail_noise_count = 16; // 默认结束噪声帧数阈值
static int aud_frame_len = 320;
static int wb_vad_speech_count;
static int wb_vad_noise_count;
static int wb_vad_start_flag;

static VAD_VARS_T *vad_state_ptr = NULL;
static float *wb_in_data = NULL;
static char *wb_buffer = NULL;
static int wb_buffer_len = 0;
static int wb_handle_frame_count = 0;

int wb_vad_enter(int start_threshold,int end_threshold,int frame_len, int silence_threshold)
{
    int ret, val;
    int in_len;
    int buf_len;
    char *temp;

    // BK_ASSERT(0 == vad_state_ptr);
    // BK_ASSERT(0 == wb_in_data);
    assert(0 == vad_state_ptr);
    assert(0 == wb_in_data);
	
    /*20ms per frame, calculate one time every 2 frames*/	
	head_speech_count = start_threshold/(20*2);
	tail_noise_count = end_threshold/(20*2);
	aud_frame_len = frame_len;
	silence_vad_count = silence_threshold/(20*2);

    val = VAD_RET_SUCCESS;

    ret = wb_vad_init(&vad_state_ptr,aud_frame_len);
    if(0 != ret)
    {
        val = VAD_RET_FAILURE;
        goto wb_exit;
    }

    in_len = aud_frame_len * sizeof(wb_in_data[0]);
    buf_len = aud_frame_len * sizeof(wb_buffer[0]);

    // temp = (char *)os_zalloc(in_len + buf_len);    
    // BK_ASSERT(temp);
    temp = (char *)malloc(in_len + buf_len);
    assert(temp);

    if(0 == temp)
    {
        val = VAD_RET_FAILURE;

        wb_vad_exit(&vad_state_ptr);
        goto wb_exit;
    }

    wb_in_data = (float *)temp;
    wb_buffer = (char *)((int *)temp + in_len);
    wb_buffer_len = 0;
    wb_handle_frame_count = 0;

    wb_estimate_init();

    fidw = fopen("dump_vad.dat", "wb");

wb_exit:
    return val;
}

int wb_vad_handler(char *buffer) 
{
    int type;
    short temp;

    // BK_ASSERT(wb_in_data != NULL);
    assert(wb_in_data != NULL);
    for (int i = 0; i < aud_frame_len / 2; i++)
	{
        temp = *((short *)(buffer + 2 * i));
        if (temp > 32767) 
		{
            temp -= 65536;
        }
        wb_in_data[i] = (float)temp;
    }
    type = wb_vad(vad_state_ptr, wb_in_data);
	
    return type;
}


void wb_frame_count_record(int frame_type)
{
    if(WB_FRAME_TYPE_SPEECH == frame_type)
    {
        wb_vad_speech_count ++;
    }
    else if(WB_FRAME_TYPE_NOISE == frame_type)
    {
        wb_vad_noise_count ++;
    }
}

void wb_estimate_init(void)
{
    wb_vad_speech_count = 0;
    wb_vad_noise_count = 0;
    wb_vad_start_flag = 0;
}

/*
return val: 0:vad none
			1:vad start
			2:vad end;
			3:vad silence	
*/
int wb_vad_estimate(void)
{
    int ret = VAD_OCCUR_NULL;
	static int no_sound_cnt = 0;
	
    if(wb_vad_start_flag)
    {
        if(wb_vad_speech_count)
        {
            wb_vad_noise_count = 0;
			no_sound_cnt = 0;
            wb_vad_speech_count = 0;
        }
		else
		{
			no_sound_cnt++;
			if(no_sound_cnt == silence_vad_count)
			{
				ret = 3;
		        // no_sound_cnt = 0;
			}
		}
		
        if(wb_vad_noise_count >= tail_noise_count)
        {
            ret = 2;//vad end
        }
    }
    else
    {
		no_sound_cnt = 0;
        if(wb_vad_noise_count)
        {
            wb_vad_speech_count = 0;
            wb_vad_noise_count = 0;
        }
	
        if(wb_vad_speech_count >= head_speech_count)
        {
            wb_vad_start_flag ++;
            // VAD_PRTF("vad_start_flag\r\n");
            printf("vad_start_flag\r\n");
            wb_vad_noise_count = 0;
			ret = 1;//vad start
        }
    }
    if (wb_vad_start_flag!=0 && ret == 0) {
		ret = 1;
	}
	else if (wb_vad_start_flag!=0 && ret == 1) {
		ret = 0;
	}
    return ret;
}

int wb_vad_get_frame_len(void)
{
    return aud_frame_len;
}

#if 0
int wb_vad_entry(char *buffer, int len)
{
    int ret;
    int frm_type;
    int backup_count;
    int vad_flag = 0;
    int head_used_count;

    frm_type = WB_FRAME_TYPE_NULL;
    if(wb_buffer_len + len >= aud_frame_len)
    {
        head_used_count = (aud_frame_len - wb_buffer_len);
        memcpy(&wb_buffer[wb_buffer_len], buffer, head_used_count);

        wb_handle_frame_count ++;
        if(wb_handle_frame_count & 0x01)
        {
            ret = wb_vad_handler(wb_buffer);
            frm_type = ret ? WB_FRAME_TYPE_SPEECH : WB_FRAME_TYPE_NOISE;
        }

        backup_count = len - head_used_count;
        if(backup_count)
        {
            memcpy(wb_buffer, &buffer[head_used_count], backup_count);
        }
        wb_buffer_len = len - head_used_count;
    }
    else
    {
        memcpy(&wb_buffer[wb_buffer_len], buffer, len);
        wb_buffer_len += len;
    }

    if(WB_FRAME_TYPE_NULL != frm_type)
    {
        wb_frame_count_record(frm_type);
        vad_flag = wb_vad_estimate();
    }

    return vad_flag;
}
#endif

int wb_vad_process(char *buffer, int len)
{
    int ret;
    int frm_type;    
    int vad_flag = 0;

    frm_type = WB_FRAME_TYPE_NULL;

    short *buf_p = (short *)buffer;
	for (int i=0; i<len/2; i++) { /*only support 320 samples*/
		short xx = *(buf_p+i);
		wb_in_data[i] = (float)xx;
	}
    ret = wb_vad(vad_state_ptr, wb_in_data);
    frm_type = ret ? WB_FRAME_TYPE_SPEECH : WB_FRAME_TYPE_NOISE;

    if(WB_FRAME_TYPE_NULL != frm_type)
    {
        wb_frame_count_record(frm_type);
        vad_flag = wb_vad_estimate();
    }

    fwrite(&ret, 2, 1, fidw);

    return vad_flag;
}


void wb_vad_deinit(void)
{
	if(vad_state_ptr != NULL)
	{
    	wb_vad_exit(&vad_state_ptr);
    	vad_state_ptr = NULL;
	}
	if(wb_in_data != NULL)
	{
    	// os_free(wb_in_data);
        free(wb_in_data);
    	wb_in_data = NULL;
		wb_buffer = NULL;
	}
    wb_buffer_len = 0;
    wb_estimate_init();

    fclose(fidw);
}

// eof

